package serialize

import (
	"fmt"
	"reflect"
	"strings"
)

var (
	// DefaulKey 默认标签名
	DefaulTag = "json"

	// ErrorUnaccceptType 错误：格式化过程中遇到不支持类型
	ErrorUnacceptType = fmt.Errorf("Unaccept type: reflect.Chan/reflect.Func")
)

// JSONMarshal 接收符合类型的任意值，并将其格式化为JSON
func JSONMarshal(v interface{}) ([]byte, error) {
	// 判断是否为不支持的类型
	if UnacceptedType(v) {
		return nil, ErrorUnacceptType
	}
	if !IsStruct(v) {
		ret, err := New(v).Embeded(reflect.ValueOf(v))
		if err != nil {
			return []byte{}, err
		}
		return []byte(ret), nil
		// return nil, errors.New("arg is not a struct(or a pointer to struct)")
	}
	ret, err := New(v).Marshal()
	if err != nil {
		return []byte{}, err
	}
	return []byte(ret), nil
}

// ValueWithTag 对reflect.Value进行封装，令其附带Tag
type ValueWithTag struct {
	Value reflect.Value // 值
	Tag   string        // 标签
}

// New 返回Value的ValueWithTag封装，若Value为指针会获取最终指向的值
func New(s interface{}) *ValueWithTag {
	v := reflect.ValueOf(s)

	for v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	return &ValueWithTag{
		Value: v,
		Tag:   DefaulTag,
	}
}

// IsStruct 判断一个Value是否为结构体
func IsStruct(s interface{}) bool {
	v := reflect.ValueOf(s)

	if v.Kind() == reflect.Invalid {
		return false
	} else {
		return v.Kind() == reflect.Struct
	}
}

// Marshal 根据ValueWithTag的Tag来解析结构体
func (s *ValueWithTag) Marshal() (string, error) {
	var parseStrs []string
	var err error
	fields := s.StructFields()

	for _, field := range fields {

		// 字段名
		name := field.Name
		// 字段值
		val := s.Value.FieldByName(name)

		// 字段值的json string
		var valStr string = ""
		// 解析Tag
		tagName, tagOpts := ParseTag(field.Tag.Get(s.Tag))
		// 解析Tag name
		if tagName != "" {
			// 如果额外指定名字，则更新字段名
			name = tagName
		}
		// 解析Tag omitempty
		if tagOpts.HasOpt("omitempty") {
			// 如果有选项omitempty
			// 且当前字段值为空值，则忽略当前字段
			zero := reflect.Zero(val.Type()).Interface()
			current := val.Interface()

			if reflect.DeepEqual(current, zero) {
				continue
			}
		}

		// 解析Tag 如果没有omitnested，代表需要递归解析
		if tagOpts.HasOpt("omitnested") {
			continue
		}
		valStr, err = s.Embeded(val)
		if err != nil {
			return "", err
		}

		if field.Anonymous && val.Kind() == reflect.Struct {
			// 如果是匿名字段并且是结构体
			// str的模式为{xx:xx, ..}
			// 我们要去掉首尾括号
			valStr = valStr[1 : len(valStr)-1]
		} else {
			valStr = fmt.Sprintf("\"%s\":%s", name, valStr)
		}

		parseStrs = append(parseStrs, valStr)
	}
	if len(parseStrs) == 0 {
		return "", nil
	}
	return fmt.Sprintf("{%s}", strings.Join(parseStrs, ",")), nil
}

// Embeded 递归处理字段值并返回格式化字符串
func (s *ValueWithTag) Embeded(val reflect.Value) (string, error) {
	if UnacceptedType(val.Interface()) {
		return "", ErrorUnacceptType
	}

	v := reflect.ValueOf(val.Interface())
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	switch v.Kind() {

	case reflect.Struct:
		// 结构体
		n := New(val.Interface())
		n.Tag = s.Tag
		nStr, err := n.Marshal()
		if err != nil {
			return "", err
		}
		return nStr, nil

	case reflect.Map:
		// Map
		var parseStrs []string
		// get the element type of the map
		mapElem := val.Type()
		switch val.Type().Kind() {
		case reflect.Ptr, reflect.Array, reflect.Map,
			reflect.Slice, reflect.Chan:
			mapElem = val.Type().Elem()
			if mapElem.Kind() == reflect.Ptr {
				mapElem = mapElem.Elem()
			}
		}

		for _, k := range val.MapKeys() {
			vStr, err := s.Embeded(val.MapIndex(k))
			if err != nil {
				return "", err
			}
			kStr := fmt.Sprintf("\"%v\":%s", k, vStr)
			parseStrs = append(parseStrs, kStr)
		}

		return fmt.Sprintf("{%s}", strings.Join(parseStrs, ",")), nil

	case reflect.Slice, reflect.Array:
		// 切片或者数组
		var parseStrs []string
		for i := 0; i < val.Len(); i++ {
			vStr, err := s.Embeded(val.Index(i))
			if err != nil {
				return "", err
			}
			parseStrs = append(parseStrs, vStr)
		}
		return fmt.Sprintf("[%s]", strings.Join(parseStrs, ",")), nil
	default:
		// nothing just FormatAtom
	}

	return FormatAtom(val), nil
}

// FormatAtom 对基本类型进行格式化
func FormatAtom(v reflect.Value) string {
	switch v.Kind() {
	case reflect.Invalid:
		return "Invalid"
	case reflect.String:
		return fmt.Sprintf("\"%v\"", v)
	default:
		return fmt.Sprintf("%v", v)
	}
}

// UnacceptedType 判断是否是不支持的类型，目前不支持reflect.Chan, reflect.Func
func UnacceptedType(i interface{}) bool {
	v := reflect.TypeOf(i)
	switch v.Kind() {
	case reflect.Chan, reflect.Func:
		return true
	}
	return false
}

// StructFields 返回结构体中导出的字段
func (s *ValueWithTag) StructFields() []reflect.StructField {
	t := s.Value.Type()

	var f []reflect.StructField

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		// 我们无法访问未导出字段的值
		if field.PkgPath != "" {
			continue
		}
		f = append(f, field)
	}

	return f
}
